#include "stdafx.h"

#include "fontmanager.h"

void MakeDisplayList( FT_Face face, char character, GLuint listBase, GLuint *textureBase ) 
{
	if( FT_Load_Glyph( face, FT_Get_Char_Index( face, character ), FT_LOAD_DEFAULT ) )
	{
		std::cerr << "Failed to load glyph library" << std::endl;
	}

    FT_Glyph glyph;
    if( FT_Get_Glyph( face->glyph, &glyph ) )
	{
		std::cerr << "Could not fetch glyph" << std::endl;
	}

	FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
    FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph;

	FT_Bitmap &bitmap = bitmap_glyph->bitmap;

	int width = NextPowerOf2( bitmap.width );
	int height = NextPowerOf2( bitmap.rows );

	GLubyte* expandedData = new GLubyte[ 2 * width * height];

	for( int j = 0 ; j < height ; ++j ) 
	{
		for( int i = 0 ; i < width ; ++i )
		{
			expandedData[2 * (i + j * width)]= expandedData[2 * (i + j * width) + 1] = (i >= bitmap.width || j >= bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width * j];
		}
	}

    glBindTexture( GL_TEXTURE_2D, textureBase[character] );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expandedData );

    delete[] expandedData;

	glNewList( listBase + character, GL_COMPILE );
		glBindTexture( GL_TEXTURE_2D, textureBase[character] );

		glPushMatrix();

		glTranslatef( bitmap_glyph->left, 0, 0 );
		glTranslatef( 0, bitmap_glyph->top-bitmap.rows, 0 );

		float x = (float) bitmap.width / (float) width, y = (float) bitmap.rows / (float) height;

		glColor4ub( 255, 255, 255, 255 );

		glBegin( GL_QUADS );
			glTexCoord2d( 0, 0 ); glVertex2f( 0, bitmap.rows );
			glTexCoord2d( 0, y ); glVertex2f( 0, 0 );
			glTexCoord2d( x, y ); glVertex2f( bitmap.width, 0 );
			glTexCoord2d( x, 0 ); glVertex2f( bitmap.width, bitmap.rows );
		glEnd();

		glPopMatrix();
		glTranslatef( face->glyph->advance.x >> 6 , 0, 0 );
	glEndList();
}

FontManager::Font::Font( const char *filename, unsigned int Height )
{
	textures = new GLuint[128];
	height = Height;
	error = false;

	FT_Library library;
	if( FT_Init_FreeType( &library ) )
	{
		std::cerr << "Could not initialze freetype" << std::endl;

		error = true;

		return;
	}

	FT_Face face;

	if( FT_New_Face( library, filename, 0, &face ) ) 
	{
		std::cerr << "Could not create face from font: " << filename << std::endl;

		error = true;

		return;
	}

	FT_Set_Char_Size( face, Height << 6, Height << 6, 96, 96);

	base = glGenLists( 128 );
	glGenTextures( 128, textures );

	for( unsigned char i = 0 ; i < 128 ; ++i )
	{
		MakeDisplayList( face, i, base, textures );
	}

	FT_Done_Face( face );
	FT_Done_FreeType( library );
}

FontManager::Font::~Font( void )
{
	glDeleteLists( base, 128 );
	glDeleteTextures( 128, textures );

	delete[] textures;
}

FontManager::FontManager( const int fonts )
{
	mFonts = new Font*[fonts];

	mnFonts = fonts;
	mnCurrentFonts = 0;
}

int FontManager::LoadFont( const char *filename, const int size )
{
	mFonts[mnCurrentFonts++] = new Font( filename, size );

	if( mFonts[mnCurrentFonts - 1]->error )
	{
		return -1;
	}

	return mnFonts;
}

FontManager::~FontManager( void )
{
	for( int i = 0 ; i < mnFonts ; ++i )
	{
		delete mFonts[i];
	}

	delete[] mFonts;
}

void FontManager::Print( const int rId, const float x, const float y, const char *fmt, bool multiline, ... )
{
	Font *font = mFonts[rId];

	if( font == 0 )
	{
		std::cerr << "Tried to print using a non-existant font" << std::endl ;
		return;
	}

	glPushAttrib( GL_TRANSFORM_BIT );
		GLint viewport[4];
		glGetIntegerv( GL_VIEWPORT, viewport );

		glMatrixMode( GL_PROJECTION );
		glPushMatrix();

		glLoadIdentity();

		gluOrtho2D( viewport[0], viewport[2], viewport[1], viewport[3] );
	glPopAttrib();
	
	GLuint fontResource = font->base;
	float h = font->height * 1.587f;
	
	char text[256];
	char lines[5][50];
	
	va_list	ap;

	// Retrieve text from argument list
	if( fmt == NULL )
	{
		*text = 0;
	}
	else 
	{
		va_start( ap, fmt );
			vsprintf( text, fmt, ap );
		va_end( ap );
	}

	// If multiline is turned on, parse the text into multiple lines delimited by newline character
	if( multiline )
	{
		int line = 0, horizontal = 0;

		while( (*text)++ != '\0' )
		{
			if( (*text) == '\n' )
			{
				lines[++line][(horizontal = 0)++] = (*text);
			}
			else
			{
				lines[line][horizontal++] = (*text);
			}
		}
	}

	glPushAttrib( GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT );	
		glMatrixMode( GL_MODELVIEW );

		glDisable( GL_LIGHTING );

		glEnable( GL_TEXTURE_2D );
		glDisable( GL_DEPTH_TEST );

		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );	

		glListBase( fontResource );

		float modelviewMatrix[16];	
		glGetFloatv( GL_MODELVIEW_MATRIX, modelviewMatrix );

		glColor4ub( 255, 255, 255, 255 );

		if( multiline )
		{
			for( int i = 0 ; i < 5 ; ++i ) 
			{
				glPushMatrix();
					glLoadIdentity();
					glTranslatef( x, y - h * i, 0 );
					glMultMatrixf( modelviewMatrix );
					glCallLists( strlen( lines[i] ), GL_UNSIGNED_BYTE, lines[i] );
				glPopMatrix();
			}
		}
		else
		{
			glPushMatrix();
				glLoadIdentity();
				glTranslatef( x, y, 0 );
				glMultMatrixf( modelviewMatrix );
				glCallLists( strlen( text ), GL_UNSIGNED_BYTE, text );
			glPopMatrix();
		}
	glPopAttrib();		

	glPushAttrib( GL_TRANSFORM_BIT );
		glMatrixMode( GL_PROJECTION );
		glPopMatrix();
	glPopAttrib();
}
